package it.niedermann.android.markdown.markwon.handler

import android.text.Editable
import android.text.Spanned
import io.noties.markwon.base.Markwon
import io.noties.markwon.base.core.spans.HeadingSpan
import io.noties.markwon.base.editor.EditHandler
import io.noties.markwon.base.editor.PersistedSpans
import io.noties.markwon.theme.MarkwonTheme

class HeadingEditHandler : EditHandler<HeadingSpan> {
    private var theme: MarkwonTheme? = null
    override fun init(markwon: Markwon) {
        theme = markwon.configuration().theme
    }

    override fun configurePersistedSpans(builder: PersistedSpans.Builder) {
        builder.persistSpan(Heading1Span::class.java, PersistedSpans.SpanFactory<Heading1Span> {
            Heading1Span(
                theme!!
            )
        })
        builder.persistSpan(Heading2Span::class.java, PersistedSpans.SpanFactory<Heading2Span> {
            Heading2Span(
                theme!!
            )
        })
        builder.persistSpan(Heading3Span::class.java, PersistedSpans.SpanFactory<Heading3Span> {
            Heading3Span(
                theme!!
            )
        })
        builder.persistSpan(Heading4Span::class.java, PersistedSpans.SpanFactory<Heading4Span> {
            Heading4Span(
                theme!!
            )
        })
        builder.persistSpan(Heading5Span::class.java, PersistedSpans.SpanFactory<Heading5Span> {
            Heading5Span(
                theme!!
            )
        })
        builder.persistSpan(Heading6Span::class.java, PersistedSpans.SpanFactory<Heading6Span> {
            Heading6Span(
                theme!!
            )
        })
    }

    override fun handleMarkdownSpan(
        persistedSpans: PersistedSpans,
        editable: Editable,
        input: String,
        span: HeadingSpan,
        spanStart: Int,
        spanTextLength: Int
    ) {
        val newSpan: HeadingSpan
        newSpan = when (span.level) {
            1 -> persistedSpans[Heading1Span::class.java]
            2 -> persistedSpans[Heading2Span::class.java]
            3 -> persistedSpans[Heading3Span::class.java]
            4 -> persistedSpans[Heading4Span::class.java]
            5 -> persistedSpans[Heading5Span::class.java]
            6 -> persistedSpans[Heading6Span::class.java]
            else -> return
        }
        val newStart = getNewSpanStart(input, spanStart)
        val newEnd = findEnd(input, newStart, newSpan.level)
        editable.setSpan(
            newSpan,
            newStart,
            newEnd,
            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )
    }

    private fun findEnd(input: String, searchFrom: Int, spanLevel: Int): Int {
        var end = searchFrom + spanLevel
        val strLength = input.length
        while (end < strLength - 1) {
            end++
            if (input[end] == '\n') {
                break
            }
        }
        return end + 1
    }

    private fun getNewSpanStart(input: String, spanStart: Int): Int {
        var start = spanStart
        while (start >= 0 && input[start] != '\n') {
            start--
        }
        start += 1
        return start
    }

    override fun markdownSpanType(): Class<HeadingSpan> {
        return HeadingSpan::class.java
    }

    private class Heading1Span(theme: MarkwonTheme) : HeadingSpan(theme, 1)
    private class Heading2Span(theme: MarkwonTheme) : HeadingSpan(theme, 2)
    private class Heading3Span(theme: MarkwonTheme) : HeadingSpan(theme, 3)
    private class Heading4Span(theme: MarkwonTheme) : HeadingSpan(theme, 4)
    private class Heading5Span(theme: MarkwonTheme) : HeadingSpan(theme, 5)
    private class Heading6Span(theme: MarkwonTheme) : HeadingSpan(theme, 6)
}